Sužinokite, kaip JavaScript vykdymas veikia kiekvieną naršyklės atvaizdavimo proceso etapą, ir optimizavimo strategijas.
Naršyklės atvaizdavimo procesas: kaip JavaScript veikia svetainės našumą
Naršyklės atvaizdavimo procesas yra žingsnių seka, kurią atlieka žiniatinklio naršyklė, kad HTML, CSS ir JavaScript kodą paverstų vizualiu atvaizdavimu vartotojo ekrane. Supratimas, kaip veikia šis procesas, yra labai svarbus kiekvienam žiniatinklio kūrėjui, siekiančiam kurti didelio našumo žiniatinklio programas. JavaScript, būdamas galinga ir dinamiška kalba, daro didelę įtaką kiekvienam šio proceso etapui. Šiame straipsnyje bus gilinamasi į naršyklės atvaizdavimo procesą ir nagrinėjama, kaip „JavaScript“ vykdymas veikia našumą, pateikiant praktinių optimizavimo strategijų.
Naršyklės atvaizdavimo proceso supratimas
Atvaizdavimo procesą galima plačiai suskirstyti į šiuos etapus:
- HTML analizavimas: Naršyklė analizuoja HTML žymėjimą ir sukuria Document Object Model (DOM), medžio struktūrą, atspindinčią HTML elementus ir jų tarpusavio santykius.
- CSS analizavimas: Naršyklė analizuoja CSS stiliaus lenteles (tiek išorines, tiek vidines) ir sukuria CSS Object Model (CSSOM), kitą medžio struktūrą, atspindinčią CSS taisykles ir jų savybes.
- Priskyrimas: Naršyklė sujungia DOM ir CSSOM, kad sukurtų atvaizdavimo medį (Render Tree). Atvaizdavimo medis apima tik tuos mazgus, kurie reikalingi turiniui rodyti, išskiriant tokius elementus kaip <head> ir elementus su `display: none`. Kiekvienam matomam DOM mazgui priskiriamos atitinkamos CSSOM taisyklės.
- Išdėstymas (perskaičiavimas): Naršyklė apskaičiuoja kiekvieno Atvaizdavimo medžio elemento padėtį ir dydį. Šis procesas taip pat žinomas kaip „reflow“ (perskaičiavimas).
- Piešimas (perspiešimas): Naršyklė nupiešia kiekvieną Atvaizdavimo medžio elementą ekrane, naudodama apskaičiuotą išdėstymo informaciją ir pritaikytus stilius. Šis procesas taip pat žinomas kaip „repaint“ (perspiešimas).
- Sudėjimas: Naršyklė sujungia skirtingus sluoksnius į galutinį vaizdą, kuris bus rodomas ekrane. Šiuolaikinės naršyklės dažnai naudoja aparatinę pagreitinimo funkciją sudėjimui, pagerindamos našumą.
JavaScript įtaka atvaizdavimo procesui
„JavaScript“ gali daryti didelę įtaką atvaizdavimo procesui įvairiuose etapuose. Prastai parašytas arba neefektyvus „JavaScript“ kodas gali sukelti našumo trukdžius, lemiančius lėtą puslapio įkėlimo laiką, „jank“ animacijas ir prastą vartotojo patirtį.
1. Blokuojamas analizatorius
Kai naršyklė HTML dokumente susiduria su <script> žyma, ji paprastai sustabdo HTML dokumento analizavimą, kad atsisiųstų ir įvykdytų „JavaScript“ kodą. Taip yra todėl, kad „JavaScript“ gali modifikuoti DOM, o naršyklė turi užtikrinti, kad DOM būtų atnaujintas prieš tęsiant. Šis blokavimo elgesys gali žymiai uždelsti pradinį puslapio atvaizdavimą.
Pavyzdys:
Apsvarstykite scenarijų, kai jūsų HTML dokumento <head> dalyje yra didelis „JavaScript“ failas:
<!DOCTYPE html>
<html>
<head>
<title>Mano svetainė</title>
<script src="large-script.js"></script>
</head>
<body>
<h1>Sveiki atvykę į mano svetainę</h1>
<p>Čia yra turinio.</p>
</body>
</html>
Šiuo atveju naršyklė sustabdys HTML analizavimą ir palauks, kol bus atsisiųstas ir įvykdytas `large-script.js` failas, prieš atvaizduodama <h1> ir <p> elementus. Tai gali sukelti pastebimą pradinio puslapio įkėlimo vėlavimą.
Sprendimai, kaip sumažinti analizatoriaus blokavimą:
- Naudokite `async` arba `defer` atributus: `async` atributas leidžia scenarijui atsisiųsti neužblokuojant analizatoriaus, o scenarijus bus įvykdytas, kai tik bus atsisiųstas. `defer` atributas taip pat leidžia scenarijui atsisiųsti neužblokuojant analizatoriaus, tačiau scenarijus bus įvykdytas po to, kai baigsis HTML analizavimas, eilės tvarka, kuria jie pasirodo HTML.
- Įdėkite scenarijus į <body> žymos pabaigą: Įdėdami scenarijus į <body> žymos pabaigą, naršyklė gali analizuoti HTML ir sukurti DOM prieš susitikdama su scenarijais. Tai leidžia naršyklei greičiau atvaizduoti pradinį puslapio turinį.
Pavyzdys naudojant `async`:
<!DOCTYPE html>
<html>
<head>
<title>Mano svetainė</title>
<script src="large-script.js" async></script>
</head>
<body>
<h1>Sveiki atvykę į mano svetainę</h1>
<p>Čia yra turinio.</p>
</body>
</html>
Šiuo atveju naršyklė atsisiųs `large-script.js` failą asinchroniškai, neužblokuodama HTML analizavimo. Scenarijus bus įvykdytas, kai tik bus atsisiųstas, galbūt dar prieš visą HTML dokumentą.
Pavyzdys naudojant `defer`:
<!DOCTYPE html>
<html>
<head>
<title>Mano svetainė</title>
<script src="large-script.js" defer></script>
</head>
<body>
<h1>Sveiki atvykę į mano svetainę</h1>
<p>Čia yra turinio.</p>
</body>
</html>
Šiuo atveju naršyklė atsisiųs `large-script.js` failą asinchroniškai, neužblokuodama HTML analizavimo. Scenarijus bus įvykdytas po to, kai bus analizuojamas visas HTML dokumentas, eilės tvarka, kuria jis pasirodo HTML.
2. DOM manipuliavimas
„JavaScript“ dažnai naudojamas DOM manipuliavimui, elementų ir jų atributų pridėjimui, pašalinimui ar modifikavimui. Dažni ar sudėtingi DOM manipuliavimai gali sukelti perskaičiavimus ir perspiešimus, kurie yra brangios operacijos ir gali žymiai paveikti našumą.
Pavyzdys:
<!DOCTYPE html>
<html>
<head>
<title>DOM manipuliavimo pavyzdys</title>
</head>
<body>
<ul id="myList">
<li>1 elementas</li>
<li>2 elementas</li>
</ul>
<script>
const myList = document.getElementById('myList');
for (let i = 3; i <= 10; i++) {
const listItem = document.createElement('li');
listItem.textContent = `Elementas ${i}`;
myList.appendChild(listItem);
}
</script>
</body>
</html>
Šiame pavyzdyje scenarijus prideda aštuonis naujus sąrašo elementus prie nenusinelys sąrašo. Kiekviena `appendChild` operacija sukelia perskaičiavimą ir perspiešimą, nes naršyklė turi perskaičiuoti išdėstymą ir perpiešti sąrašą.
Sprendimai, kaip optimizuoti DOM manipuliavimą:
- Minimizuokite DOM manipuliavimus: Kiek įmanoma sumažinkite DOM manipuliavimų skaičių. Vietoj DOM modifikavimo kelis kartus, pabandykite pakeitimus suskirstyti į grupes.
- Naudokite DocumentFragment: Sukurkite DocumentFragment, atlikite visus DOM manipuliavimus fragmentui, o tada fragmentą priskirkite faktiniam DOM vieną kartą. Tai sumažina perskaičiavimų ir perspiešimų skaičių.
- Saugyklos DOM elementai: Venkite nuolat ieškoti tuose pačiuose DOM elementuose. Išsaugokite elementus kintamuosiuose ir pakartotinai naudokite juos.
- Naudokite efektyvius selektorius: Naudokite konkrečius ir efektyvius selektorius (pvz., ID), kad taikytumėte elementus. Venkite naudoti sudėtingus ar neefektyvius selektorius (pvz., nereikalingai naršant DOM medį).
- Venkite nereikalingų perskaičiavimų ir perspiešimų: Tam tikros CSS savybės, pvz., `width`, `height`, `margin` ir `padding`, keičiantis gali sukelti perskaičiavimus ir perspiešimus. Stenkitės vengti dažnai keisti šias savybes.
Pavyzdys naudojant DocumentFragment:
<!DOCTYPE html>
<html>
<head>
<title>DOM manipuliavimo pavyzdys</title>
</head>
<body>
<ul id="myList">
<li>1 elementas</li>
<li>2 elementas</li>
</ul>
<script>
const myList = document.getElementById('myList');
const fragment = document.createDocumentFragment();
for (let i = 3; i <= 10; i++) {
const listItem = document.createElement('li');
listItem.textContent = `Elementas ${i}`;
fragment.appendChild(listItem);
}
myList.appendChild(fragment);
</script>
</body>
</html>
Šiame pavyzdyje visi nauji sąrašo elementai pirmiausia pridedami prie DocumentFragment, o tada fragmentas pridedamas prie nenusinelys sąrašo. Tai sumažina perskaičiavimų ir perspiešimų skaičių iki vieno.
3. Brangios operacijos
Tam tikros „JavaScript“ operacijos yra savaime brangios ir gali paveikti našumą. Tai apima:
- Sudėtingi skaičiavimai: Sudėtingų matematinių skaičiavimų ar duomenų apdorojimo atlikimas „JavaScript“ gali sunaudoti reikšmingus CPU išteklius.
- Didelės duomenų struktūros: Darbas su dideliais masyvais ar objektais gali padidinti atminties naudojimą ir sulėtinti apdorojimą.
- Reguliariosios išraiškos: Sudėtingos reguliariosios išraiškos gali lėtai vykdytis, ypač dideliuose tekstuose.
Pavyzdys:
<!DOCTYPE html>
<html>
<head>
<title>Brangios operacijos pavyzdys</title>
</head>
<body>
<div id="result"></div>
<script>
const resultDiv = document.getElementById('result');
let largeArray = [];
for (let i = 0; i < 1000000; i++) {
largeArray.push(Math.random());
}
const startTime = performance.now();
largeArray.sort(); // Brangi operacija
const endTime = performance.now();
const executionTime = endTime - startTime;
resultDiv.textContent = `Vykdymo laikas: ${executionTime} ms`;
</script>
</body>
</html>
Šiame pavyzdyje scenarijus sukuria didelį atsitiktinių skaičių masyvą ir jį surūšiuoja. Didelio masyvo rūšiavimas yra brangi operacija, kuri gali užtrukti.
Sprendimai, kaip optimizuoti brangias operacijas:
- Optimizuokite algoritmus: Naudokite efektyvius algoritmus ir duomenų struktūras, kad sumažintumėte reikiamo apdorojimo kiekį.
- Naudokite Web Workers: Brangias operacijas perkelti į Web Workers, kurie veikia fone ir neužblokuoja pagrindinio gijos.
- Išsaugokite rezultatus: Išsaugokite brangių operacijų rezultatus, kad jų nereikėtų perskaičiuoti kiekvieną kartą.
- Debouncing ir Throttling: Įdiekite debouncing arba throttling technikas, kad apribotumėte funkcijų iškvietimų dažnumą. Tai naudinga įvykių tvarkytuvams, kurie iškviečiami dažnai, pvz., slinkimo arba dydžio keitimo įvykiams.
Pavyzdys naudojant Web Worker:
<!DOCTYPE html>
<html>
<head>
<title>Brangios operacijos pavyzdys</title>
</head>
<body>
<div id="result"></div>
<script>
const resultDiv = document.getElementById('result');
if (window.Worker) {
const myWorker = new Worker('worker.js');
myWorker.onmessage = function(event) {
const executionTime = event.data;
resultDiv.textContent = `Vykdymo laikas: ${executionTime} ms`;
};
myWorker.postMessage(''); // Paleiskite darbininką
} else {
resultDiv.textContent = 'Web Workers nepalaikomi šioje naršyklėje.';
}
</script>
</body>
</html>
worker.js:
self.onmessage = function(event) {
let largeArray = [];
for (let i = 0; i < 1000000; i++) {
largeArray.push(Math.random());
}
const startTime = performance.now();
largeArray.sort(); // Brangi operacija
const endTime = performance.now();
const executionTime = endTime - startTime;
self.postMessage(executionTime);
}
Šiame pavyzdyje rūšiavimo operacija atliekama Web Worker, kuris veikia fone ir neužblokuoja pagrindinio gijos. Tai leidžia UI likti reaguojančiam, kol vyksta rūšiavimas.
4. Trečiųjų šalių scenarijai
Daugelis žiniatinklio programų remiasi trečiųjų šalių scenarijais analitikai, reklamai, socialinės medijos integracijai ir kitoms funkcijoms. Šie scenarijai dažnai gali būti reikšmingas našumo pertekliaus šaltinis, nes jie gali būti prastai optimizuoti, atsisiųsti didelius duomenų kiekius arba atlikti brangias operacijas.
Pavyzdys:
<!DOCTYPE html>
<html>
<head>
<title>Trečiosios šalies scenarijaus pavyzdys</title>
<script src="https://example.com/analytics.js"></script>
</head>
<body>
<h1>Sveiki atvykę į mano svetainę</h1>
<p>Čia yra turinio.</p>
</body>
</html>
Šiame pavyzdyje scenarijus įkelia analitinį scenarijų iš trečiosios šalies domeno. Jei šis scenarijus lėtai įkeliamas ar vykdomas, tai gali neigiamai paveikti puslapio našumą.
Sprendimai, kaip optimizuoti trečiųjų šalių scenarijus:
- Įkelkite scenarijus asinchroniškai: Naudokite `async` arba `defer` atributus, kad įkeltumėte trečiųjų šalių scenarijus asinchroniškai, neužblokuojant analizatoriaus.
- Įkelkite scenarijus tik tada, kai reikia: Įkelkite trečiųjų šalių scenarijus tik tada, kai jie tikrai reikalingi. Pavyzdžiui, socialinės medijos valdiklius įkelkite tik tada, kai vartotojas su jais sąveikauja.
- Naudokite Content Delivery Network (CDN): Naudokite CDN, kad trečiųjų šalių scenarijus teiktumėte iš vietos, kuri yra geografiškai arti vartotojo.
- Stebėkite trečiųjų šalių scenarijų našumą: Naudokite našumo stebėjimo įrankius, kad stebėtumėte trečiųjų šalių scenarijų našumą ir nustatytumėte bet kokius trukdžius.
- Apsvarstykite alternatyvas: Ieškokite alternatyvių sprendimų, kurie gali būti našesni arba turėti mažesnį pėdsaką.
5. Įvykių klausytojai
Įvykių klausytojai leidžia „JavaScript“ kodui reaguoti į vartotojo sąveikas ir kitus įvykius. Tačiau per daug įvykių klausytojų prijungimas arba neefektyvių įvykių tvarkytuvų naudojimas gali paveikti našumą.
Pavyzdys:
<!DOCTYPE html>
<html>
<head>
<title>Įvykių klausytojo pavyzdys</title>
</head>
<body>
<ul id="myList">
<li>1 elementas</li>
<li>2 elementas</li>
<li>3 elementas</li>
</ul>
<script>
const listItems = document.querySelectorAll('#myList li');
for (let i = 0; i < listItems.length; i++) {
listItems[i].addEventListener('click', function() {
alert(`Jūs paspaudėte elementą ${i + 1}`);
});
}
</script>
</body>
</html>
Šiame pavyzdyje scenarijus prideda paspaudimo įvykio klausytoją prie kiekvieno sąrašo elemento. Nors tai veikia, tai nėra efektyviausias metodas, ypač jei sąraše yra daug elementų.
Sprendimai, kaip optimizuoti įvykių klausytojus:
- Naudokite įvykių delegavimą: Vietoj įvykių klausytojų priskyrimo atskiriems elementams, priskirkite vieną įvykio klausytoją prie pagrindinio elemento ir naudokite įvykių delegavimą, kad tvarkytumėte įvykius jo vaikams.
- Pašalinkite nereikalingus įvykių klausytojus: Pašalinkite įvykių klausytojus, kai jie nebereikalingi.
- Naudokite efektyvius įvykių tvarkytuvus: Optimizuokite kodą savo įvykių tvarkytuvuose, kad sumažintumėte reikiamo apdorojimo kiekį.
- Ribokite arba debouncing įvykių tvarkytuvus: Naudokite ribojimo arba debouncing technikas, kad apribotumėte įvykių tvarkytuvo iškvietimų dažnumą, ypač dažnai iškviečiamų įvykių, pvz., slinkimo arba dydžio keitimo įvykių, atveju.
Pavyzdys naudojant įvykių delegavimą:
<!DOCTYPE html>
<html>
<head>
<title>Įvykių klausytojo pavyzdys</title>
</head>
<body>
<ul id="myList">
<li>1 elementas</li>
<li>2 elementas</li>
<li>3 elementas</li>
</ul>
<script>
const myList = document.getElementById('myList');
myList.addEventListener('click', function(event) {
if (event.target.tagName === 'LI') {
const index = Array.prototype.indexOf.call(myList.children, event.target);
alert(`Jūs paspaudėte elementą ${index + 1}`);
}
});
</script>
</body>
</html>
Šiame pavyzdyje prie nenusinelys sąrašo pridedamas vienas paspaudimo įvykio klausytojas. Kai paspaudžiamas sąrašo elementas, įvykio klausytojas tikrina, ar įvykio tikslas yra sąrašo elementas. Jei taip, įvykio klausytojas tvarko įvykį. Šis metodas yra efektyvesnis nei atskiro paspaudimo įvykio klausytojo priskyrimas kiekvienam sąrašo elementui.
Įrankiai „JavaScript“ našumui matuoti ir gerinti
Yra keletas įrankių, padedančių matuoti ir gerinti „JavaScript“ našumą:
- Naršyklės kūrėjų įrankiai: Šiuolaikinėse naršyklėse yra integruoti kūrėjų įrankiai, leidžiantys profiliuoti „JavaScript“ kodą, nustatyti našumo trukdžius ir analizuoti atvaizdavimo procesą.
- Lighthouse: Lighthouse yra atviro kodo, automatizuotas įrankis, skirtas pagerinti žiniatinklio puslapių kokybę. Jis turi našumo, prieinamumo, progresinių žiniatinklio programų, SEO ir kt. auditus.
- WebPageTest: WebPageTest yra nemokamas įrankis, leidžiantis testuoti jūsų svetainės našumą iš skirtingų vietų ir naršyklių.
- PageSpeed Insights: PageSpeed Insights analizuoja žiniatinklio puslapio turinį, tada pateikia pasiūlymus, kaip padaryti tą puslapį greitesnį.
- Našumo stebėjimo įrankiai: Yra keletas komercinių našumo stebėjimo įrankių, kurie gali padėti jums stebėti jūsų žiniatinklio programos našumą realiu laiku.
Išvada
„JavaScript“ atlieka svarbų vaidmenį naršyklės atvaizdavimo procese. Suprasti, kaip „JavaScript“ vykdymas veikia našumą, yra būtina kuriant didelio našumo žiniatinklio programas. Laikydamiesi šiame straipsnyje išdėstytų optimizavimo strategijų, galite sumažinti „JavaScript“ įtaką atvaizdavimo procesui ir užtikrinti sklandžią bei reaguojančią vartotojo patirtį. Nepamirškite visada matuoti ir stebėti savo svetainės našumo, kad nustatytumėte ir pašalintumėte bet kokius trukdžius.
Šis vadovas suteikia tvirtą pagrindą suprasti „JavaScript“ įtaką naršyklės atvaizdavimo procesui. Toliau tyrinėkite ir eksperimentuokite su šiomis technikomis, kad patobulintumėte savo žiniatinklio kūrimo įgūdžius ir sukurtumėte išskirtines vartotojo patirtis pasaulinei auditorijai.